package org.maven.cscodegen.plugin;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.xteam.cs.model.ErrorMark;
import org.xteam.cs.model.ErrorMarkConsoleDiagnostic;
import org.xteam.cs.model.FileResource;
import org.xteam.cs.model.IProgressMonitor;
import org.xteam.cs.model.Project;
import org.xteam.cs.model.ProjectManager;
import org.xteam.cs.model.ProjectProperties;
import org.xteam.cs.model.ProjectResource;
import org.xteam.cs.runtime.IErrorReporter;

/**
 * Compiler Studio Code Generation Tool
 * 
 * @goal generate
 * @phase generate-sources
 * @requiresProject
 */
public class CompilerStudioGenerateMojo extends AbstractMojo {

	/**
	 * Path where the CompilerStudio projects are located
	 * 
	 * @parameter expression="${cscodegen.projectRoot}"
	 *            default-value="${basedir}/src/main/cs"
	 */
	private File projectRoot;
	
	/**
	 * Path where the generated sources should be placed
	 * 
	 * @parameter expression="${cscodegen.sourceRoot}"
	 *            default-value="${project.build.directory}/generated-sources/cs"
	 * @required
	 */
	private File sourceRoot;
	
	/**
	 * Path where the generated resources should be placed
	 * 
	 * @parameter expression="${cscodegen.sourceRoot}"
	 *            default-value="${project.build.directory}/generated-resources/cs"
	 * @required
	 */
	private File resourceRoot;

	/**
	 * A list of project files to include. Can contain ant-style wildcards and
	 * double wildcards. Defaults to *.wsdl
	 * 
	 * @parameter
	 */
	private String includes[];

	/**
	 * A list of project files to exclude. Can contain ant-style wildcards and
	 * double wildcards.
	 * 
	 * @parameter
	 */
	private String excludes[];

	/**
	 * @parameter expression="${project}"
	 * @required
	 */
	private MavenProject project;
	
	/**
	 * Verbose mode
	 * 
	 * @parameter expression="${cscodegen.verbose}"
	 *            default-value="false"
	 */
	private boolean verbose;

	public void execute() throws MojoExecutionException {
		if (includes == null) {
			includes = new String[] { "*.cpj" };
		}
		 
		List<File> projectFiles = getProjectFiles(projectRoot, includes, excludes);
		
		 if (projectFiles.size() == 0) {
			 getLog().info("Nothing to generate");
			 return;
		 }
		
		ProjectManager manager = new ProjectManager();
		Project csProject = new Project(manager);
		
		for (File projectFile : projectFiles) {
			csProject.open(projectFile);
			manager.buildProject(csProject, new ProgressMonitor());
			boolean hasError = false;
			for (ProjectResource resource : csProject.getResources()) {
				List<ErrorMark> marks = resource.getMarks(ErrorMark.class);
				for (ErrorMark mark : marks) {
					if (mark.getLevel() == IErrorReporter.ERROR)
						hasError |= true;
				}
				ErrorMarkConsoleDiagnostic diag = new ErrorMarkConsoleDiagnostic(System.out);
				try {
					diag.printDiagnostic(new FileReader(((FileResource)resource).getFile()), marks);
				} catch (IOException e) {
				}
			}
			if (! hasError) {
				ProjectProperties props = (ProjectProperties)csProject.getProperties();
				props.sourceFolder = sourceRoot.getAbsolutePath();
				props.useResourceFolder = true;
				props.resourceFolder = resourceRoot.getAbsolutePath();
				csProject.generate(new ProgressMonitor());
			}
		}

		if (project != null && sourceRoot != null && sourceRoot.exists()) {
			project.addCompileSourceRoot(sourceRoot.getAbsolutePath());
		}
		
		if (project != null && resourceRoot != null && resourceRoot.exists()) {
			Resource resource = new Resource();
			resource.setDirectory(resourceRoot.getAbsolutePath());
			resource.setFiltering(false);
			project.addResource(resource);
		}
	}

	private static List<File> getProjectFiles(File dir, String includes[],
			String excludes[]) throws MojoExecutionException {
		
		if (dir == null) {
            return new ArrayList<File>();
        }

        if (!dir.exists()) {
            throw new MojoExecutionException(dir + " does not exist");
        }

		List<String> exList = new ArrayList<String>();
		if (excludes != null) {
			exList.addAll(Arrays.asList(excludes));
		}
		exList.addAll(Arrays.asList(org.codehaus.plexus.util.FileUtils
				.getDefaultExcludes()));

		String inc = joinWithComma(includes);
		String ex = joinWithComma(exList.toArray(new String[exList.size()]));

		try {
			return (List<File>)org.codehaus.plexus.util.FileUtils.getFiles(dir,
					inc, ex);
		} catch (IOException exc) {
			throw new MojoExecutionException(exc.getMessage(), exc);
		}
	}

	private static String joinWithComma(String[] arr) {
		if (arr == null) {
			return "";
		}
		StringBuilder str = new StringBuilder();

		if (arr != null) {
			for (String s : arr) {
				if (str.length() > 0) {
					str.append(',');
				}
				str.append(s);
			}
		}
		return str.toString();
	}
	
	private class ProgressMonitor implements IProgressMonitor {

		@Override
		public void beginTask(String msg, int amountOfWork) {
			getLog().info(msg);
		}

		@Override
		public void done() {
		}

		@Override
		public void subTask(String name) {
			if (verbose)
				getLog().info("   " + name);
		}

		@Override
		public void worked(int amount) {
		}

		@Override
		public void internalWorked(double amount) {
		}
		
	}
}
